/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.primitives.unix;

import fr.x9c.cadmium.kernel.Block;
import fr.x9c.cadmium.kernel.CodeRunner;
import fr.x9c.cadmium.kernel.Primitive;
import fr.x9c.cadmium.kernel.PrimitiveProvider;
import fr.x9c.cadmium.kernel.Value;

/**
 * This class provides implementation for 'unix_error_message' primitive.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
@PrimitiveProvider
public final class Errmsg {

    /** Identifier for 'E2BIG' unix error. */
    private static final int E2BIG = 0;

    /** Identifier for 'EACCES' unix error. */
    private static final int EACCES = 1;

    /** Identifier for 'EAGAIN' unix error. */
    private static final int EAGAIN = 2;

    /** Identifier for 'EBADF' unix error. */
    private static final int EBADF = 3;

    /** Identifier for 'EBUSY' unix error. */
    private static final int EBUSY = 4;

    /** Identifier for 'ECHILD' unix error. */
    private static final int ECHILD = 5;

    /** Identifier for 'EDEADLK' unix error. */
    private static final int EDEADLK = 6;

    /** Identifier for 'EDOM' unix error. */
    private static final int EDOM = 7;

    /** Identifier for 'EEXIST' unix error. */
    private static final int EEXIST = 8;

    /** Identifier for 'EFAULT' unix error. */
    private static final int EFAULT = 9;

    /** Identifier for 'EFBIG' unix error. */
    private static final int EFBIG = 10;

    /** Identifier for 'EINTR' unix error. */
    private static final int EINTR = 11;

    /** Identifier for 'EINVAL' unix error. */
    private static final int EINVAL = 12;

    /** Identifier for 'EIO' unix error. */
    private static final int EIO = 13;

    /** Identifier for 'EISDIR' unix error. */
    private static final int EISDIR = 14;

    /** Identifier for 'EMFILE' unix error. */
    private static final int EMFILE = 15;

    /** Identifier for 'EMLINK' unix error. */
    private static final int EMLINK = 16;

    /** Identifier for 'ENAMETOOLONG' unix error. */
    private static final int ENAMETOOLONG = 17;

    /** Identifier for 'ENFILE' unix error. */
    private static final int ENFILE = 18;

    /** Identifier for 'ENODEV' unix error. */
    private static final int ENODEV = 19;

    /** Identifier for 'ENOENT' unix error. */
    private static final int ENOENT = 20;

    /** Identifier for 'ENOEXEC' unix error. */
    private static final int ENOEXEC = 21;

    /** Identifier for 'ENOLCK' unix error. */
    private static final int ENOLCK = 22;

    /** Identifier for 'ENOMEM' unix error. */
    private static final int ENOMEM = 23;

    /** Identifier for 'ENOSPC' unix error. */
    private static final int ENOSPC = 24;

    /** Identifier for 'ENOSYS' unix error. */
    private static final int ENOSYS = 25;

    /** Identifier for 'ENOTDIR' unix error. */
    private static final int ENOTDIR = 26;

    /** Identifier for 'ENOTEMPTY' unix error. */
    private static final int ENOTEMPTY = 27;

    /** Identifier for 'ENOTTY' unix error. */
    private static final int ENOTTY = 28;

    /** Identifier for 'ENXIO' unix error. */
    private static final int ENXIO = 29;

    /** Identifier for 'EPERM' unix error. */
    private static final int EPERM = 30;

    /** Identifier for 'EPIPE' unix error. */
    private static final int EPIPE = 31;

    /** Identifier for 'ERANGE' unix error. */
    private static final int ERANGE = 32;

    /** Identifier for 'EROFS' unix error. */
    private static final int EROFS = 33;

    /** Identifier for 'ESPIPE' unix error. */
    private static final int ESPIPE = 34;

    /** Identifier for 'ESRCH' unix error. */
    private static final int ESRCH = 35;

    /** Identifier for 'EXDEV' unix error. */
    private static final int EXDEV = 36;

    /** Identifier for 'EWOULDBLOCK' unix error. */
    private static final int EWOULDBLOCK = 37;

    /** Identifier for 'EINPROGRESS' unix error. */
    private static final int EINPROGRESS = 38;

    /** Identifier for 'EALREADY' unix error. */
    private static final int EALREADY = 39;

    /** Identifier for 'ENOTSOCK' unix error. */
    private static final int ENOTSOCK = 40;

    /** Identifier for 'EDESTADDRREQ' unix error. */
    private static final int EDESTADDRREQ = 41;

    /** Identifier for 'EMSGSIZE' unix error. */
    private static final int EMSGSIZE = 42;

    /** Identifier for 'EPROTOTYPE' unix error. */
    private static final int EPROTOTYPE = 43;

    /** Identifier for 'ENOPROTOOPT' unix error. */
    private static final int ENOPROTOOPT = 44;

    /** Identifier for 'EPROTONOSUPPORT' unix error. */
    private static final int EPROTONOSUPPORT = 45;

    /** Identifier for 'ESOCKTNOSUPPORT' unix error. */
    private static final int ESOCKTNOSUPPORT = 46;

    /** Identifier for 'EOPNOTSUPP' unix error. */
    private static final int EOPNOTSUPP = 47;

    /** Identifier for 'EPFNOSUPPORT' unix error. */
    private static final int EPFNOSUPPORT = 48;

    /** Identifier for 'EAFNOSUPPORT' unix error. */
    private static final int EAFNOSUPPORT = 49;

    /** Identifier for 'EADDRINUSE' unix error. */
    private static final int EADDRINUSE = 50;

    /** Identifier for 'EADDRNOTAVAIL' unix error. */
    private static final int EADDRNOTAVAIL = 51;

    /** Identifier for 'ENETDOWN' unix error. */
    private static final int ENETDOWN = 52;

    /** Identifier for 'ENETUNREACH' unix error. */
    private static final int ENETUNREACH = 53;

    /** Identifier for 'ENETRESET' unix error. */
    private static final int ENETRESET = 54;

    /** Identifier for 'ECONNABORTED' unix error. */
    private static final int ECONNABORTED = 55;

    /** Identifier for 'ECONNRESET' unix error. */
    private static final int ECONNRESET = 56;

    /** Identifier for 'ENOBUFS' unix error. */
    private static final int ENOBUFS = 57;

    /** Identifier for 'EISCONN' unix error. */
    private static final int EISCONN = 58;

    /** Identifier for 'ENOTCONN' unix error. */
    private static final int ENOTCONN = 59;

    /** Identifier for 'ESHUTDOWN' unix error. */
    private static final int ESHUTDOWN = 60;

    /** Identifier for 'ETOOMANYREFS' unix error. */
    private static final int ETOOMANYREFS = 61;

    /** Identifier for 'ETIMEDOUT' unix error. */
    private static final int ETIMEDOUT = 62;

    /** Identifier for 'ECONNREFUSED' unix error. */
    private static final int ECONNREFUSED = 63;

    /** Identifier for 'EHOSTDOWN' unix error. */
    private static final int EHOSTDOWN = 64;

    /** Identifier for 'EHOSTUNREACH' unix error. */
    private static final int EHOSTUNREACH = 65;

    /** Identifier for 'ELOOP' unix error. */
    private static final int ELOOP = 66;

    /** Identifier for 'EOVERFLOW' unix error. */
    private static final int EOVERFLOW = 67;

    /**
     * No instance of this class.
     */
    private Errmsg() {
    } // end empty constructor

    /**
     * Returns the error message corresponding to an error code.
     * @param ctxt context
     * @param err error code
     * @return the error message corresponding to given code
     */
    @Primitive
    public static Value unix_error_message(final CodeRunner ctxt,
                                           final Value err) {
        if (err.isLong()) {
            final String msg;
            switch (err.asLong()) {
            case Errmsg.E2BIG: msg = "Argument list too long"; break;
            case Errmsg.EACCES: msg = "Permission denied"; break;
            case Errmsg.EAGAIN: msg = "Resource temporarily unavailable; try again"; break;
            case Errmsg.EBADF: msg = "Bad file descriptor"; break;
            case Errmsg.EBUSY: msg = "Resource unavailable"; break;
            case Errmsg.ECHILD: msg = "No child process"; break;
            case Errmsg.EDEADLK: msg = "Resource deadlock would occur"; break;
            case Errmsg.EDOM: msg = "Domain error for math functions, etc."; break;
            case Errmsg.EEXIST: msg = "File exists"; break;
            case Errmsg.EFAULT: msg = "Bad address"; break;
            case Errmsg.EFBIG: msg = "File too large"; break;
            case Errmsg.EINTR: msg = "Function interrupted by signal"; break;
            case Errmsg.EINVAL: msg = "Invalid argument"; break;
            case Errmsg.EIO: msg = "Hardware I/O error"; break;
            case Errmsg.EISDIR: msg = "Is a directory"; break;
            case Errmsg.EMFILE: msg = "Too many open files by the process"; break;
            case Errmsg.EMLINK: msg = "Too many links"; break;
            case Errmsg.ENAMETOOLONG: msg = "Filename too long"; break;
            case Errmsg.ENFILE: msg = "Too many open files in the system"; break;
            case Errmsg.ENODEV: msg = "No such device"; break;
            case Errmsg.ENOENT: msg = "No such file or directory"; break;
            case Errmsg.ENOEXEC: msg = "Not an executable file"; break;
            case Errmsg.ENOLCK: msg = "No locks available"; break;
            case Errmsg.ENOMEM: msg = "Not enough memory"; break;
            case Errmsg.ENOSPC: msg = "No space left on device"; break;
            case Errmsg.ENOSYS: msg = "Function not supported"; break;
            case Errmsg.ENOTDIR: msg = "Not a directory"; break;
            case Errmsg.ENOTEMPTY: msg = "Directory not empty"; break;
            case Errmsg.ENOTTY: msg = "Inappropriate I/O control operation"; break;
            case Errmsg.ENXIO: msg = "No such device or address"; break;
            case Errmsg.EPERM: msg = "Operation not permitted"; break;
            case Errmsg.EPIPE: msg = "Broken pipe"; break;
            case Errmsg.ERANGE: msg = "Result too large"; break;
            case Errmsg.EROFS: msg = "Read-only file system"; break;
            case Errmsg.ESPIPE: msg = "Invalid seek e.g. on a pipe"; break;
            case Errmsg.ESRCH: msg = "No such process"; break;
            case Errmsg.EXDEV: msg = "Invalid link"; break;
            case Errmsg.EWOULDBLOCK: msg = "Operation would block"; break;
            case Errmsg.EINPROGRESS: msg = "Operation now in progress"; break;
            case Errmsg.EALREADY: msg = "Operation already in progress"; break;
            case Errmsg.ENOTSOCK: msg = "Socket operation on non-socket"; break;
            case Errmsg.EDESTADDRREQ: msg = "Destination address required"; break;
            case Errmsg.EMSGSIZE: msg = "Message too long"; break;
            case Errmsg.EPROTOTYPE: msg = "Protocol wrong type for socket"; break;
            case Errmsg.ENOPROTOOPT: msg = "Protocol not available"; break;
            case Errmsg.EPROTONOSUPPORT: msg = "Protocol not supported"; break;
            case Errmsg.ESOCKTNOSUPPORT: msg = "Socket type not supported"; break;
            case Errmsg.EOPNOTSUPP: msg = "Operation not supported on socket"; break;
            case Errmsg.EPFNOSUPPORT: msg = "Protocol family not supported"; break;
            case Errmsg.EAFNOSUPPORT: msg = "Address family not supported by protocol family"; break;
            case Errmsg.EADDRINUSE: msg = "Address already in use"; break;
            case Errmsg.EADDRNOTAVAIL: msg = "Can't assign requested address"; break;
            case Errmsg.ENETDOWN: msg = "Network is down"; break;
            case Errmsg.ENETUNREACH: msg = "Network is unreachable"; break;
            case Errmsg.ENETRESET: msg = "Network dropped connection on reset"; break;
            case Errmsg.ECONNABORTED: msg = "Software caused connection abort"; break;
            case Errmsg.ECONNRESET: msg = "Connection reset by peer"; break;
            case Errmsg.ENOBUFS: msg = "No buffer space available"; break;
            case Errmsg.EISCONN: msg = "Socket is already connected"; break;
            case Errmsg.ENOTCONN: msg = "Socket is not connected"; break;
            case Errmsg.ESHUTDOWN: msg = "Can't send after socket shutdown"; break;
            case Errmsg.ETOOMANYREFS: msg = "Too many references: can't splice"; break;
            case Errmsg.ETIMEDOUT: msg = "Connection timed out"; break;
            case Errmsg.ECONNREFUSED: msg = "Connection refused"; break;
            case Errmsg.EHOSTDOWN: msg = "Host is down"; break;
            case Errmsg.EHOSTUNREACH: msg = "No route to host"; break;
            case Errmsg.ELOOP: msg = "Too many levels of symbolic links"; break;
            case Errmsg.EOVERFLOW: msg = "File size or position not representable"; break;
            default:
                assert false : "invalid error code";
                msg = "Unknown error";
            } // end swicth
            return Value.createFromBlock(Block.createString(msg));
        } else {
            return Value.createFromBlock(Block.createString("Unknown error"));
        } // end if/else
    } // end method 'unix_error_message(CodeRunner, Value)'

} // end class 'Errmsg'
